package com.chuangjian.hire.users.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chuangjian.hire.common.controller.BaseController;
import com.chuangjian.hire.common.dto.LabelValueDTO;
import com.chuangjian.hire.common.dto.R;
import com.chuangjian.hire.common.exceptionEnum.ApiExceptionEnum;
import com.chuangjian.hire.common.exption.ApiException;
import com.chuangjian.hire.users.dao.MenuMapper;
import com.chuangjian.hire.users.dao.RoleMapper;
import com.chuangjian.hire.users.dao.RoleMenuMapper;
import com.chuangjian.hire.users.dao.UsersRoleMapper;
import com.chuangjian.hire.users.domain.MenuDO;
import com.chuangjian.hire.users.domain.RoleDO;
import com.chuangjian.hire.users.domain.RoleMenuDO;
import com.chuangjian.hire.users.domain.UsersRoleDO;
import com.chuangjian.hire.users.dto.RoleDTO;
import com.chuangjian.hire.util.BeanUtils;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import lombok.extern.java.Log;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Log
@Api(tags = "角色相关")
@RestController
@RequestMapping("/role")
public class RoleController extends BaseController {

    @Resource
    RoleMapper roleMapper;

    @Resource
    UsersRoleMapper usersRoleMapper;


    @Resource
    RoleMenuMapper roleMenuMapper;
    @Resource
    MenuMapper menuMapper;


    @PostMapping("add")
    public R add(@Validated @RequestBody RoleDTO roleDO) {

        Wrapper query = Wrappers.<RoleDO>lambdaQuery()
                .eq(RoleDO::getName, roleDO.getName());
        if (roleMapper.selectCount(query) > 1) {
            throw ApiException.builder().baseExceptionCode(ApiExceptionEnum.EXIST_CODE_ERROR).build();
        }
        roleDO.setUserCount(0);
        roleMapper.insert(roleDO);


        Optional.ofNullable(roleDO.getMenuList()).orElse(Lists.newArrayList()).stream().forEach((val) -> {

            RoleMenuDO roleMenuDO = new RoleMenuDO();
            roleMenuDO.setRoleId(roleDO.getId());
            roleMenuDO.setMenuId(val.getValue());
            roleMenuMapper.insert(
                    roleMenuDO
            );
        });


        return R.success(true);
    }

    @PostMapping("update")
    public R update(@Validated @RequestBody RoleDTO roleDO) {


        Wrapper query = Wrappers.<RoleDO>lambdaQuery()
                .eq(RoleDO::getName, roleDO.getName());

        RoleDO role = roleMapper.selectOne(query);

        if (role != null && !role.getId().equals(roleDO.getId())) {
            throw ApiException.builder().baseExceptionCode(ApiExceptionEnum.EXIST_CODE_ERROR).build();
        }

        roleMenuMapper.delete(
                Wrappers.<RoleMenuDO>lambdaQuery()
                        .eq(RoleMenuDO::getRoleId, roleDO.getId())
        );


        Optional.ofNullable(roleDO.getMenuList()).orElse(Lists.newArrayList()).stream().forEach((val) -> {

            RoleMenuDO roleMenuDO = new RoleMenuDO();
            roleMenuDO.setRoleId(roleDO.getId());
            roleMenuDO.setMenuId(val.getValue());
            roleMenuMapper.insert(
                    roleMenuDO
            );
        });
        Integer count = usersRoleMapper.selectCount(
                Wrappers.lambdaQuery(UsersRoleDO.class)
                        .eq(UsersRoleDO::getRoleId, roleDO.getId())
        );

        roleDO.setUserCount(count);


        return R.success(roleMapper.updateById(roleDO));
    }

    @PostMapping("delete")
    public R delete(@Validated @RequestBody RoleDTO roleDO) {


        Integer count = usersRoleMapper.selectCount(
                Wrappers.lambdaQuery(UsersRoleDO.class)
                        .eq(UsersRoleDO::getRoleId, roleDO.getId())
        );
        if (count > 0) {
            throw new ApiException(ApiExceptionEnum.DEFAULT_ERROR.getCode(), "角色已经绑定用户！");
        }

        roleMenuMapper.delete(
                Wrappers.lambdaQuery(RoleMenuDO.class)
                        .eq(RoleMenuDO::getRoleId, roleDO.getId())
        );


        return R.success(roleMapper.deleteById(roleDO));
    }


    @PostMapping("list")
    public R list(Page page) {

        Wrapper query = Wrappers.<RoleDO>lambdaQuery();

        Page<RoleDO> pageResult = roleMapper.selectPage(page, query);
        List<RoleDO> records = pageResult.getRecords();
        if (CollectionUtil.isEmpty(records)) {
            return R.success(pageResult);
        }

        List<Long> roleIds = records.stream().map(RoleDO::getId).collect(Collectors.toList());

        Map<Long, List<LabelValueDTO<String, Long>>> roleListByUserId = getMenuListByRoleIds(roleIds);

        List<RoleDTO> recordList = records.stream().map(it -> {
            RoleDTO users = BeanUtils.transform(RoleDTO.class, it);
            List<LabelValueDTO<String, Long>> labelValueDTOS = roleListByUserId.get(users.getId());
            users.setMenuList(labelValueDTOS);
            return users;
        }).collect(Collectors.toList());
        page.setRecords(recordList);


        return R.success(pageResult);
    }

    private Map<Long, List<LabelValueDTO<String, Long>>> getMenuListByRoleIds(List<Long> roleIds) {

        if (CollectionUtil.isEmpty(roleIds)) {
            return new HashMap<>();
        }

        Wrapper query = Wrappers.<RoleMenuDO>lambdaQuery()
                .in(RoleMenuDO::getRoleId, roleIds);

        List<RoleMenuDO> list = roleMenuMapper.selectList(query);


        Map<Long, LabelValueDTO<String, Long>> roleList = getMenuList(list.stream().map(RoleMenuDO::getMenuId).collect(Collectors.toList()));

        HashMap<Long, List<LabelValueDTO<String, Long>>> userIdAndRoleList = list.stream().collect(Collectors.groupingBy(RoleMenuDO::getRoleId, HashMap::new,
                Collectors.mapping(it -> roleList.get(it.getMenuId()), Collectors.toList())));

        return userIdAndRoleList;
    }


    private Map<Long, LabelValueDTO<String, Long>> getMenuList(List<Long> menuIds) {
        if (CollectionUtil.isEmpty(menuIds)) {
            return new HashMap<>();
        }

        Wrapper query = Wrappers.<MenuDO>lambdaQuery()
                .in(MenuDO::getId, menuIds);

        List<MenuDO> list = menuMapper.selectList(query);

        return list.stream().collect(Collectors.toMap(it -> it.getId(), it -> new LabelValueDTO<>(it.getName(), it.getId())));
    }
}
